﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sherwood.SignOn.Client.UrlFilter;

namespace Sherwood.SignOn.Client.Tests
{
    /// <summary>
    /// Tests rules engine.
    /// 
    /// Basic rules:
    /// - Url
    ///   -> Urls are expected to be regular expressions.
    ///      .* is expected to be automatically appended / prepended to regular
    ///      expressions that do not begin with ^ / end with $
    /// - Users
    ///   -> Empty = .* = not checked
    ///   -> Regular expression = any matching username
    /// - Roles
    ///   -> Empty = .* = not checked
    ///   -> Regular expression = any matching role
    /// - AlwaysDeny
    ///   -> true = deny access (overrides any rules that allow access -- no further rules are checked)
    ///   -> false (default) = allow access to any matching username / roles (unless overridden by another rule)
    /// - AlwaysAllow
    ///   -> true = allow access (i.e. last rule checked)
    ///   -> false (default) = other rules are checked (allows access unless overridden by another rule)
    /// - Rule-order
    ///   -> General rules should occur before more specific rules.
    ///   -> Rules are read in the order they appear in the configuration file.
    ///   -> The LAST rule that matches the url is that one that will be applied, unless the rule
    ///      an alwaysAllow or alwaysDeny rule which matches url/username/roles is encountered.
    /// </summary>
    [TestClass]
    public class UrlFilterTests
    {
        [TestMethod]
        public void Public_Domain_With_Anonymous_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "sherwood.codeplex.com";
            string username = null;
            string[] roles = null;
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);


            //Assert
            Assert.IsTrue(access, "Anonymous user should have access to public domain");

        }


   

        [TestMethod]
        public void Public_SubDomain_With_Anonymous_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "subdomain.sherwood.codeplex.com";
            string username = "";
            string[] roles = new string[]{};
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "Anonymous user should have access to public subdomain");

        }

        [TestMethod]
        public void Unknown_Domain_Anonymous_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "somewhere.unknown.com";
            string username = "";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "Anonymous user should have access to unknown subdomain");

        }


        [TestMethod]
        public void Protected_Domain_Anonymous_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "secure.sherwood.codeplex.com";
            string username = "";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsFalse(access, "Anonymous user should not have access to protected domain");

        }

        [TestMethod]
        public void Protected_Domain_Some_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "secure.sherwood.codeplex.com";
            string username = "username";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "User should have access to protected domain");
        }


        [TestMethod]
        public void Protected_Domain_with_full_path_Some_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "secure.sherwood.codeplex.com/folder/file.aspx";
            string username = "username";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "User should have access to protected domain");
        }


        [TestMethod]
        public void Protected_Domain_with_image_exception_Anonymous_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "secure.sherwood.codeplex.com/folder/file.jpg";
            string username = "";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "Annonymous users should have access to jpgs");
        }


        [TestMethod]
        public void Admin_Domain_Non_Admin_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "admin.sherwood.codeplex.com/";
            string username = "username";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsFalse(access, "Only 'administrator' should have access to the admin page.");
        }


        [TestMethod]
        public void Admin_Domain_Admin_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "admin.sherwood.codeplex.com/";
            string username = "administrator";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "'administrator' should have access to the admin page.");
        }

        [TestMethod]
        public void Admin_Domain_Admin_User_Wrong_Case_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "admin.sherwood.codeplex.com/";
            string username = "ADMinistrator";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "'ADMinistrator' should have access to the admin page - regular expressions should not be case sensitive.");
        }


        [TestMethod]
        public void Edit_Domain_Anonymouse_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "edit.sherwood.codeplex.com";
            string username = "";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsFalse(access, "Anonymous users should not should have access to the edit page.");
        }

        [TestMethod]
        public void Edit_Domain_User_Without_Roles_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "edit.sherwood.codeplex.com";
            string username = "administrator";
            string[] roles = new string[] { };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsFalse(access, "No user without the correct roles should should have access to the edit page.");
        }

        [TestMethod]
        public void Edit_Domain_User_With_Editor_Role_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "edit.sherwood.codeplex.com";
            string username = "someuser";
            string[] roles = new string[] { "editor" };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "User with the editor should should have access to the edit page.");
        }


        [TestMethod]
        public void Edit_Domain_User_With_Editor_Role_And_Denied_Role_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "edit.sherwood.codeplex.com";
            string username = "someuser";
            string[] roles = new string[] { "editor", "under18" };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsFalse(access, "User should be denied because user has under18 role.");
        }


        [TestMethod]
        public void Allow_Rule_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "edit.sherwood.codeplex.com";
            string username = "systemadmin";
            string[] roles = new string[] {  };
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty);

            url = "admin.sherwood.codeplex.com";
            bool access1 = filter.CheckAccess(url, username, "", roles, string.Empty);

            url = "secure.sherwood.codeplex.com";
            bool access2 = filter.CheckAccess(url, username, "", roles, string.Empty);

            //Assert
            Assert.IsTrue(access, "User should be allowed access to everything because of AlwaysAllow rule.");
            Assert.IsTrue(access1, "User should be allowed access to everything because of AlwaysAllow rule.");
            Assert.IsTrue(access2, "User should be allowed access to everything because of AlwaysAllow rule.");
        }


        [TestMethod]
        public void Redirect_Rule_With_Denied_User_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "redirect.sherwood.codeplex.com";
            string username = "";
            string[] roles = new string[] { };
            string redirect = null;
            bool requireNonPersistentSession = false;
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty, out redirect, out requireNonPersistentSession);


            //Assert
            Assert.IsFalse(access, "User should not have access to domain");
            Assert.IsTrue(redirect == "denyredirect");
        }

        [TestMethod]
        public void Redirect_Rule_With_Allowed_Role_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "redirect.sherwood.codeplex.com";
            string username = "";
            string[] roles = new string[] { "allowrole" };
            string redirect = null;
            bool requireNonPersistentSession = false;
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty, out redirect, out requireNonPersistentSession);


            //Assert
            Assert.IsTrue(access, "User should have access to domain");
            Assert.IsTrue(redirect == "allowredirect");
        }

        [TestMethod]
        public void Redirect_Denied_User_Explicit_Deny_Rule_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "redirect.sherwood.codeplex.com";
            string username = "";
            string[] roles = new string[] {  };
            string redirect = null;
            bool requireNonPersistentSession = false;
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty, out redirect, out requireNonPersistentSession);


            //Assert
            Assert.IsFalse(access, "User should not have access to domain");
            Assert.IsTrue(redirect == "denyredirect");
        }


        [TestMethod]
        public void Redirect_Allowed_User_Explicit_Allow_Rule_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "redirect.sherwood.codeplex.com";
            string username = "allowuser";
            string[] roles = new string[] { };
            string redirect = null;
            bool requireNonPersistentSession = false;
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty, out redirect, out requireNonPersistentSession);


            //Assert
            Assert.IsTrue(access, "User should have access to domain");
            Assert.AreEqual<string>(redirect,"allowredirect");
        }

        /// <summary>
        /// Error page urls should be treated as if they existed (error page url removed).
        /// </summary>
        [TestMethod]
        public void Error_404_Url_Test()
        {
            //Assemble
            string rawUrl = "http://sherwood.codeplex.com/Pages/handler404.aspx?404;http://sherwood.codeplex.com:80/getattachment/e3511c2a-7c0f-4d00-8238-b2f268042787/image.png?maxsidesize=104";
            string expectedOrigUrl = "http://sherwood.codeplex.com/getattachment/e3511c2a-7c0f-4d00-8238-b2f268042787/image.png?maxsidesize=104";

            //Act
            string origUrl = UrlFilterModule.GetOriginalUrl(rawUrl);

            //Assert
            Assert.AreEqual<string>(expectedOrigUrl, origUrl);
        }


        [TestMethod]
        public void Access_Proveded_To_User_Host()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "internal.sherwood.codeplex.com";
            string host = "127.0.0.1";
            string username = "";
            string[] roles = new string[] { };
            string redirect = null;
            bool requireNonPersistentSession = false;
            bool access = filter.CheckAccess(url, username, host, roles, string.Empty, out redirect, out requireNonPersistentSession);


            //Assert
            Assert.IsTrue(access, "User should have access to domain");
            Assert.AreEqual<string>(redirect, "allowRedirectHost");
        }



        [TestMethod]
        public void Access_Denied_To_User_Host()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "internal.sherwood.codeplex.com";
            string host = "192.168.0.1";
            string username = "";
            string[] roles = new string[] { };
            string redirect = null;
            bool requireNonPersistentSession = false;
            bool access = filter.CheckAccess(url, username, host, roles, string.Empty, out redirect, out requireNonPersistentSession);


            //Assert
            Assert.IsFalse(access, "User should not have access to domain");
            Assert.AreEqual<string>(redirect, "denyRedirectHost");
        }

        [TestMethod]
        public void Redirect_Denied_User_Deny_Rule_Test()
        {
            //Assemble
            UrlFilterEngine filter = new UrlFilterEngine();

            //Act
            string url = "redirect.sherwood.codeplex.com";
            string username = "denyuser";
            string[] roles = new string[] { };
            string redirect = null;
            bool requireNonPersistentSession = false;
            bool access = filter.CheckAccess(url, username, "", roles, string.Empty, out redirect, out requireNonPersistentSession);


            //Assert
            Assert.IsFalse(access, "User should not have access to domain");
            Assert.AreEqual<string>(redirect, "denyredirect");
        }


        public void Performance_Test()
        {
            DateTime startTime = DateTime.Now;

            for (int i = 0; i < 10000; i++)
            {
                //Assemble
                UrlFilterEngine filter = new UrlFilterEngine();

                //Act
                string url = "sherwood.codeplex.com";
                string username = "";
                string[] roles = new string[] { };
                string redirect = null;
                bool requireNonPersistentSession = false;
                bool access = filter.CheckAccess(url, username, "", roles, string.Empty, out redirect, out requireNonPersistentSession);
            }

            Console.WriteLine("Time taken for 10000 requests: " + (DateTime.Now - startTime).TotalSeconds.ToString() + "s");


        }




    }
}
